--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
--  file:    weapondefs_post.lua
--  brief:   weaponDef post processing
--  author:  Dave Rodgers
--
--  Copyright (C) 2008.
--  Licensed under the terms of the GNU GPL, v2 or later.
--
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

local modOptions
if (Spring.GetModOptions) then
  modOptions = Spring.GetModOptions()
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
-- Utility
--

local spEcho = Spring.Echo
local lower  = string.lower
local format = string.format
local floor  = math.floor
local rad    = math.rad
local sin    = math.sin
local cos    = math.cos

local function isTable(x)  return (type(x) == 'table')  end
local function isString(x) return (type(x) == 'string') end

local UnitDefs = DEFS.unitDefs  --needed

VFS.Include("luarules/utils/table_utils.lua")

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
--  Per-unitDef weaponDefs
--

local MAX_WEAPONS_PER_UNIT = 32

local function GetWeaponName (udName, weaponName) 
  if (isString(weaponName)) then
    local lowerName = lower(weaponName)  --upper?
    local fullName = udName .. '_' .. lowerName
    if (WeaponDefs[fullName]) then
      return fullName
    elseif (WeaponDefs[lowerName]) then
      return lowerName
    else
      spEcho('[weapondefs_post.lua] Error: No weapon of name "' .. weaponName .. '" (in unit "' .. udName .. '")')    
      return nil
    end  
  end	
end

-- process the UnitDefs
for udName, ud in pairs(UnitDefs) do
  -- add this unitDef's weaponDefs
  if (ud.weapondefs) then
    local wds = ud.weapondefs
    if (isTable(wds)) then
      for wdName, wd in pairs(wds) do
        if (isTable(wd)) then
          local fullName = udName .. '_' .. tostring(wdName)
          WeaponDefs[fullName] = wd
          wd.filename = ud.filename
        end  
      end
    end  
  end  

  -- convert the weapon names
  local weapons = ud.weapons
  if (isTable(weapons)) then
    for i = 1, MAX_WEAPONS_PER_UNIT do
      local w = weapons[i]
      if (isString(w)) then
        w = { def = w }
        weapons[i] = w
      end  
      if (isTable(w)) then
        w.name = GetWeaponName(udName, w.def)
        if (w.name) then
          --spEcho()(udName .. ' ' .. w.name .. ' ' .. w.def)
          w.def = nil
        else
          weapons[i] = nil
        end  
      else
        weapons[i] = nil
      end    
    end
  end
    
  -- convert the death explosions
  ud.explodeas = GetWeaponName(udName, ud.explodeas)
  ud.selfdestructas = GetWeaponName(udName, ud.selfdestructas)      
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

--VFS.Include("luarules/utils/debug.lua")
--for tableName, wd in pairs(WeaponDefs) do printTable ("WeaponDefs["..tableName.."]", wd) end
--for tableName, ud in pairs(UnitDefs) do printTable ("UnitDefs["..tableName.."].weapons", ud.weapons) end  

-- fix CustomParams
for wdName, wd in pairs(WeaponDefs) do
  if (isTable(wd)) then
    wd.customparams = wd.customparams or {}
  else
    Spring.Echo('[weapondefs_post.lua] Error: Invalid weaponDef: WeaponDefs['..wdName..']')
    WeaponDefs[wdName] = nil
  end      
end  
  
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
 
-- print DPS
local weaponDPS = {}
local weaponClass = {}
local dpsStrings = {}
  
local function GetWeaponClass(targetCat)
  targetCat = targetCat and targetCat:upper() or "TANK PLANE"
   
  if (targetCat:find("WATER")) then
    return "Water"
  elseif (targetCat:find("SUB")) then
    if (targetCat:find("BUILDING") or targetCat:find("SHIP")) then
      return "Water"
    else  
      return "Underwater"
    end  
  elseif (targetCat:find("BUILDING") or targetCat:find("SHIP") or targetCat:find("TANK") or targetCat:find("WEAK_ARMOR")) then
    if (targetCat:find("PLANE")) then
      return "Multipurpose"
    elseif (targetCat:find("HELICOPTER")) then
      return "Ground +"      
    else  
      return "Ground"
    end 
  else      
    return "AntiAir"
  end
end

  
  for tableName, wd in pairsByKeys(WeaponDefs) do
    local upperName = tableName:upper()
    local damage = wd.damage and wd.damage.default or 0
    local reload = wd.reloadtime or 1.0
    local dps = damage / reload
    local text = " DPS = " .. damage
    if (wd.projectiles and wd.projectiles > 1) then
	    dps = dps * wd.projectiles
	    text = text .. " * " .. wd.projectiles
    end
    if (wd.burst and wd.burst > 1) then
	    dps = dps * wd.burst
	    text = text .. " * " .. wd.burst
    end

    dpsStrings[#dpsStrings + 1] = {
      upperName .. ":",
      text .. " / " .. format("%.2g", reload),
      " = " .. floor(dps + 0.5)
    }  

    weaponDPS[tableName] = dps
    weaponClass[tableName] = GetWeaponClass(wd.customparams.onlytargetcategory)    
  end
  
  alignValues (dpsStrings, 1)
  alignValues (dpsStrings, 2)
  spEcho()
  spEcho()
  spEcho("=== WEAPONS DPS ===")
  for _, s in ipairs(dpsStrings) do
    spEcho(s[1] .. s[2] .. s[3])
  end
  spEcho()
  spEcho()
  
  --[[
  for tableName, _ in pairsByKeys(WeaponDefs) do
    local upperName = tableName:upper()
    spEcho(upperName .. " " .. weaponClass[tableName])
  end
  spEcho()
  spEcho()
  --]]
  
  classesInOrder = { "Underwater", "Water", "Ground", "Ground +", "Multipurpose", "AntiAir" }
  
spEcho("=== UNITS DPS ===")
for udName, ud in pairsByKeys(UnitDefs) do
  local weapons = ud.weapons
  if (isTable(weapons)) then
    local totalDPS = 0
    local dpsByClass = {}
      
    local circles = ud.customparams.rangecircles or {}
    local addedCircle = false
      
    for i = 1, MAX_WEAPONS_PER_UNIT do
      local w = weapons[i]
      if (w) then
        --w.onlytargetcategory =  or WeaponDefs[w.name].customparams.onlytargetcategory 
        --local upperName = w.name:upper()
        local name = w.name
        local dps = weaponDPS[name]
        local wClass = w.onlytargetcategory and GetWeaponClass(w.onlytargetcategory) or weaponClass[name]
        totalDPS = totalDPS + dps
        dpsByClass[wClass] = (dpsByClass[wClass] or 0) + dps
          
          
        -- weaponDef badTargetCategory and onlyTargetCategory
        local wd = WeaponDefs[name]
        local params = wd.customparams 
        w. badtargetcategory = w. badtargetcategory or params. badtargetcategory 
        w.onlytargetcategory = w.onlytargetcategory or params.onlytargetcategory  

        -- weapon mainDir
        if (w.maindirangle) then
          if (type(w.maindirangle) == "number" and not isTable(w.maindir)) then
            local angle = rad(w.maindirangle)
            w.maindir = { sin(angle), 0, cos(angle) }
          end
          w.maindirangle = nil
        end
 
        -- weapon range circles
        if (w.rangecircle) then
          circles[wd.range] = (type(w.rangecircle) == "string") and w.rangecircle or (params.rangecolor or wClass)
          addedCircle = true
        end           
      end  
    end
    
    if (addedCircle) then
      ud.customparams.rangecircles = circles
    end
      
    
    local dpsStrings = { { "[TOTAL]: ", floor(totalDPS + 0.5) } }
    for _, c in ipairs(classesInOrder) do
      local dps = dpsByClass[c]
      if (dps) then
        dpsStrings[#dpsStrings + 1] = { c .. ": ", floor(dps + 0.5) }  
      end
    end
    alignValues (dpsStrings, 1)
     
    spEcho(udName:upper() .. ":")
    for _, s in ipairs(dpsStrings) do
      spEcho(" " .. s[1] .. s[2])
    end
    spEcho()
  end
end  
spEcho()
 
-- remove params that are temporary and shouldn't be loaded 
for tableName, wd in pairs(WeaponDefs) do
  params = wd.customparams

  params.rangecolor = params.rangecolor or weaponClass[tableName]
  params.numberofbarrels    = nil
  params.barrelspenalty     = nil
  params.badtargetcategory  = nil
  params.onlytargetcategory = nil
end

--VFS.Include("luarules/utils/debug.lua")
--for tableName, ud in pairs(UnitDefs) do printTable ("UnitDefs["..tableName.."]", ud.customparams) end 


-- customParams = {
  -- armorPenetration   = 100,
  -- impactDamage = {
    -- default = 100,
  -- },
  
  -- badTargetCategory  = [[BUILDING]],
  -- onlyTargetCategory = [[BUILDING TANK SHIP]],
  
  -- isSpecialTorpedo   = true,
      
  -- minimumRange       = 100,
  -- supportedAccuracy  = 100,

  -- salvoReloadTime    = 20,
  -- salvoShots         = 3,
  
  -- numberOfBarrels    = 1,
  -- barrelsPenalty     = 0.5,  

  -- rangeColor         = "ARTY", 
-- },

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--
-- Fix the canAttack tag
--
--[[
do
  local processed = {}

  local function RawCanAttack (ud)
    if (ud.weapons) then
      for i, weapon in pairs(ud.weapons) do
        local wd = WeaponDefs[lower(weapon.name)]
        if ((not wd.isshield) and 
            (not wd.interceptor)) then
          return true
        end
      end
    end
    if (ud.kamikaze) then
      return not ud.yardmap
    end
    return false
  end

  local function FacCanAttack (ud)
    for _, name in pairs(ud.buildoptions) do
      if (CanAttack(UnitDefs[lower(name)])) then
        return true
      end
    end
    return false
  end

  local function CanAttack (ud)
    if (processed[ud] ~= nil) then
      return processed[ud]
    end
    local canAttack = false
    if (RawCanAttack(ud)) then
      canAttack = true
    elseif (ud.tedclass == 'PLANT') then
      if (FacCanAttack(ud)) then
        canAttack = true
      end
    end
    processed[ud] = canAttack
    return canAttack
  end

  -- loop through the unit defs
  for name, ud in pairs(UnitDefs) do
    ud.canattack = CanAttack(ud)
  end
end
--]]

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------